#include "ProcesoCoordinador.h"
#include "Fifo.h"
#include "Mazo.h"
#include "Carta.h"
#include "Canales.h"
#include "Logger.h"
#include "FuncionesAuxiliares.h" // avisar, recibirAviso
#include <sys/wait.h>
#include <iostream>	//cout

#define CANTCARTAS 4
#define NOMBREPRIMERJUGADOR "1"

using namespace std;


ProcesoCoordinador::ProcesoCoordinador(char* totalJugadores, char* debug) :
		ProcesoJugador((char*) NOMBREPRIMERJUGADOR, totalJugadores, debug) {	
	//Abro los canales con el estadistico.
	aEstad = new Fifo(COORDESTAD);
	deEstad = new Fifo(ESTADCOORD);
 	ganadorRonda = 0;
	// Abro los canales de comunicación.
	cantJugadores = atoi(totalJugadores);
	aJugadores = vector<Comunicador*>();
	deJugadores = vector<Comunicador*>(); 		
	for (int i = 2; i <= cantJugadores; i++) {
		string nombre = itos(i);
		string nombreFifoAJugador = string(COORDJUGADOR) + nombre;
		string nombreFifoDeJugador = string(JUGADORCOORD) + nombre;
		aJugadores.push_back(new Fifo( nombreFifoAJugador.c_str() ));
		deJugadores.push_back(new Fifo( nombreFifoDeJugador.c_str() ));
	}
}


ProcesoCoordinador::~ProcesoCoordinador() {
}

int ProcesoCoordinador::correr() {
	
	string remitente = "COORD " + this->jugadorNombre;
	
	abrirCanalEscrituraCarta();
	abrirCanalLecturaCarta();
		
	recibirOKRecibidor();
	Logger::loggear("Recibe OK Recibidor",remitente);		
	
	recibirOKJugadores();
	Logger::loggear("Recibe OK Jugadores",remitente);
	
	// Aviso que estoy listo para comenzar la partida.
	avisar(aEstad,OK);
	
	Aviso seguirJuego = recibirAviso(deEstad);
	Logger::loggear("Recibe Aviso de inicio de juego",remitente);
	avisarJugadores(OK);
	
	while (	seguirJuego.getTipo() == OK ){
		Logger::loggear("Reparte Cartas",remitente);
		repartirCartas();
		avisarJugadores(OK);
		Logger::loggear("Avisar jugadores de arrancar ronda",remitente);
		
		int ganoAlguien = 0;
		while ( ! ganoAlguien) {
			pasarCarta();
			recibirCarta();
			
			Logger::loggear("Juega mano y verifica estado Jugadores",remitente);
			
			ganoAlguien = verificarEstadoJugadores();
			
			if ( (this->jugador).gano() ) {
				ganoAlguien = 1;
				ganadorRonda = 1;
			}
			
			if ( ganoAlguien ) {
				Logger::loggear("Aviso que ganó alguien",remitente);
				avisarJugadores(FINDERONDA);
				avisarEstadisticoGanador();				
			} else {
				Logger::loggear("Aviso que NO ganó nadie",remitente);
				avisarJugadores(OK);
			}
			
		}
		
		jugador.reiniciarRonda();
		// Verifico si sigue el juego.
		seguirJuego = recibirAviso(deEstad);
		Logger::loggear("Recibo anuncio de si debe seguir el juego",remitente);
		
		// Retrasmito la señal del Estadistico [ OK | FINDELJUEGO ].
		avisarJugadores( seguirJuego.getTipo() );
	}
	
	matarRecibidorSiguiente();
	
	cerrarCanalLectura();
	cerrarCanalesLectura();
	
	wait(NULL);	// procRecibidor.
	wait(NULL); // procPerdedor.
	
	cerrarCanalEscritura();
	
	cerrarCanalesEscritura();
	Logger::loggear("Finaliza",remitente);
		
	return 0;
}


void ProcesoCoordinador::repartirCartas() {
	Mazo mazo(cantJugadores);
	mazo.barajar(ganadorRonda);
	for (int i = 0; i < CANTCARTAS; i++) {
		Carta aux = mazo.getCarta();
		this->jugador.tomarCarta(aux);
	}
	for (size_t i = 0; i < aJugadores.size(); i++) {
		for (int j = 0; j < CANTCARTAS; j++) {
			Carta carta = mazo.getCarta();
			int tamanioBuffer;
			char* bufferCarta = carta.serializar(&tamanioBuffer);
			aJugadores[i]->escribir(bufferCarta, tamanioBuffer);
			delete[] bufferCarta;
			
			// Espera el ACK.
			verificarEstadoJugador(i);
		}
	}	
}


void ProcesoCoordinador::avisarJugador(int indiceJugador, int tipoAviso) {
	avisar(aJugadores[indiceJugador], tipoAviso);
}


void ProcesoCoordinador::avisarJugadores(int tipoAviso) {
	for (size_t i = 0; i < aJugadores.size(); i++) {
		avisarJugador(i,tipoAviso);
	}
}


void ProcesoCoordinador::avisarEstadisticoGanador() {
	avisar(aEstad,ganadorRonda);
}


/* Debido a que identifico un unico ganador, si hubiera más de uno 
 * por ronda, los no identificados pueden "perder". */
int ProcesoCoordinador::verificarEstadoJugadores() {
	int resultado = 0;
	for (size_t i = 0; i < deJugadores.size(); i++) {
		int estado = verificarEstadoJugador(i);
		if (estado == GANO) {
			// Se suma 2 porque el vector es 0-numerated y los jugadores
			// son 1-numerated. El jugador 1 es el mismo coordinador
			ganadorRonda = i + 2;
			resultado = 1;
		}
	}
	return resultado;
}


int ProcesoCoordinador::verificarEstadoJugador(int indiceJugador) {
	return recibirAviso(deJugadores[indiceJugador]).getTipo();
}


void ProcesoCoordinador::cerrarCanalesLectura() {
	for (size_t i = 0; i < deJugadores.size(); i++)
		deJugadores[i]->cerrar();
	deEstad->cerrar();
}


// Los cierra y los elimina
void ProcesoCoordinador::cerrarCanalesEscritura() {
	for (size_t i = 0; i < aJugadores.size(); i++) {
		aJugadores[i]->cerrar();
		aJugadores[i]->eliminar();
	}
	aEstad->cerrar();
	aEstad->eliminar();
}


void ProcesoCoordinador::recibirOKJugadores() {
	for (size_t i = 0; i < deJugadores.size(); i++) {
		recibirAviso(deJugadores[i]);
	}
}
